using System.Collections.Generic;
using System.Diagnostics;
using System.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common;
using EntLog = Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Logging.Filters;
using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;
using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;

namespace EntlibAOP.Diagnostic.Logging
{
    /// <summary>
    /// Log messages to application log categorized by criticality
    /// </summary>
    public static class Logger
    {
        static readonly LogWriter logWriter;

        /// <summary>
        /// Default message priority 
        /// <remarks>It will be used when nothing else was set/configured</remarks>
        /// </summary>
        private const Priority DefaultPriority = Priority.Unknown;
        /// <summary>
        /// Default message severity
        /// <remarks>It will be used when nothing else was set/configured</remarks>
        /// </summary>
        private const TraceEventType DefaultSeverity = TraceEventType.Information;
        /// <summary>
        /// Default event ID
        /// <remarks>It will be used when nothing else was set/configured</remarks>
        /// </summary>
        private const int DefaultEventId = 1;
        /// <summary>
        /// Default event title 
        /// <remarks>It will be used when nothing else was set/configured</remarks>
        /// </summary>
        private const string DefaultTitle = "";
        /// <summary>
        /// Title for error events
        /// </summary>
        private const string ErrorTitle = "Error";
        /// <summary>
        /// Title for information events
        /// </summary>
        private const string InfoTitle = "Information";
        /// <summary>
        /// Title for warning events
        /// </summary>
        private const string WarnTitle = "Warning";
        /// <summary>
        /// Title for debug events
        /// </summary>
        private const string DebugTitle = "Debug";
        /// <summary>
        /// Default event categories
        /// <remarks>It will be used when nothing else was set/configured</remarks>
        /// </summary>
        private static readonly string[] DefaultCategory = new string[] { System.AppDomain.CurrentDomain.FriendlyName };

        #region Constructor
        static Logger()
        {

            //IConfigurationSource config = ConfigurationSourceFactory.Create();
            //LoggingSettings settings = (LoggingSettings)config.GetSection(LoggingSettings.SectionName);
            //if (settings == null)
            //{
            //    // The formatter is responsible for the
            //    // look of the message. Notice the tokens:
            //    // {timestamp}, {newline}, {message}, {category}
            //    TextFormatter formatter = new TextFormatter
            //        ("Timestamp: {timestamp}{newline}" +
            //        "Message: {message}{newline}" +
            //        "Category: {category}{newline}");

            //    // Log messages to a log file.
            //    // Use the formatter mentioned above
            //    // as well as the header and footer
            //    // specified.
            //    FlatFileTraceListener logFileListener =
            //        new FlatFileTraceListener("c:\\TextLog\\messages.log",
            //                                   "----------",
            //                                   "----------",
            //                                   formatter);

            //    // My collection of TraceListeners.
            //    // I am only using one.  Could add more.
            //    LogSource defaultLogSource =
            //        new LogSource("DefaultSource", SourceLevels.All);
            //    defaultLogSource.Listeners.Add(logFileListener);

            //    // Assigning a non-existant LogSource
            //    // for Logging Application Block
            //    // Specials Sources I don't care about.
            //    // Used to say "don't log".
            //    LogSource nonExistantLogSource = new LogSource("Empty");


            //    // I want all messages with a category of
            //    // "Error" or "Debug" to get distributed
            //    // to all TraceListeners in my defaultLogSource.
            //    IDictionary<string, LogSource> traceSources = new Dictionary<string, LogSource>();
            //    traceSources.Add("Error", defaultLogSource);
            //    traceSources.Add("Debug", defaultLogSource);

            //    // Let's glue it all together.
            //    // No filters at this time.
            //    // I won't log a couple of the Special
            //    // Sources: All Events and Events not
            //    // using "Error" or "Debug" categories.
            //    logWriter = new LogWriter(new ILogFilter[0],
            //                    traceSources,
            //                    nonExistantLogSource,
            //                    nonExistantLogSource,
            //                    defaultLogSource,
            //                    "Error",
            //                    false,
            //                    true);
            //}
            //else
            //{
            //    LogWriterFactory factory = new LogWriterFactory();
            //    logWriter = factory.Create();
            //}

        }
        #endregion

        #region Error Log Methods

        /// <summary>
        /// Log error message
        /// </summary>        
        public static void WriteError(string message)
        {
            EntLog.Logger.Write(message, DefaultCategory, (int)Priority.High, DefaultEventId, TraceEventType.Error, ErrorTitle);
        }

        /// <summary>
        /// Log error categorized error message
        /// </summary>
        /// <param name="categories" Type=string>Categories associated with the message</param>
        /// <param name="message" Type=string>Message to be logged</param>
        public static void WriteError(string[] categories, string message)
        {
            EntLog.Logger.Write(message, categories != null ? string.Join(",", categories) + ",Error" : "Error",
                 (int)Priority.High, DefaultEventId, TraceEventType.Error, ErrorTitle);
        }

        /// <summary>
        /// Log error categorized error message with extended information
        /// </summary>
        /// <param name="categories" Type=string>Categories associated with the message</param>
        /// <param name="properties" Type=IDictionary<string, object>Extended message information</param>
        /// <param name="message" Type=string>Message to be logged 
        /// <remarks>will contain additional information that you want to log
        /// </remarks>
        /// </param>
        public static void WriteError(string[] categories, IDictionary<string, object> properties, string message)
        {
            EntLog.Logger.Write(message, categories, (int)Priority.High, DefaultEventId, TraceEventType.Error, ErrorTitle, properties);
        }

        #endregion

        #region Information Log Methods
        /// <summary>
        /// Log information message
        /// </summary>
        public static void WriteInformation(string message)
        {
            EntLog.Logger.Write(message, DefaultCategory, (int)Priority.Low, DefaultEventId, TraceEventType.Information, InfoTitle);
        }

        /// <summary>
        /// Log categorized information message
        /// </summary>
        /// <param name="categories" Type=string>Categories associated with the message</param>
        /// <param name="message" Type=string>Message to be logged</param>
        public static void WriteInformation(string[] categories, string message)
        {
            EntLog.Logger.Write(message, 
                categories != null? string.Join(",",categories)+",Information":"Information", 
                (int)Priority.Low, DefaultEventId, TraceEventType.Information, InfoTitle);
        }

        /// <summary>
        /// Log categorized information message with extended information
        /// </summary>
        /// <param name="categories" Type=string>Categories associated with the message</param>
        /// <param name="properties" Type=IDictionary<string, object>Additional information to log</param>        
        /// <param name="message" Type=string>Message to be logged</param>
        public static void WriteInformation(string[] categories, IDictionary<string, object> properties, string message)
        {
            EntLog.Logger.Write(message, categories != null? string.Join(",",categories)+",Information":"Information", 
                (int)Priority.Low, DefaultEventId, TraceEventType.Information, InfoTitle, properties);
        }
        #endregion

        #region Warning Log Methods
        /// <summary>
        /// Log warning message
        /// </summary>
        public static void WriteWarning(string message)
        {
            EntLog.Logger.Write(message, DefaultCategory, (int)Priority.Medium, DefaultEventId, TraceEventType.Warning, WarnTitle);
        }

        /// <summary>
        /// Log categorized warning message
        /// </summary>
        /// <param name="categories" Type=string>Categories associated with the message</param>
        /// <param name="message" Type=string>Message to be logged</param>
        public static void WriteWarning(string[] categories, string message)
        {
            EntLog.Logger.Write(message, categories != null ? string.Join(",", categories) + ",Warning" : "Warning",
                (int)Priority.Medium, DefaultEventId, TraceEventType.Warning, WarnTitle);
        }

        /// <summary>
        /// Log categoriezed warning message with extended information
        /// </summary>
        /// <param name="categories" Type=string>Categories associated with the message</param>
        /// <param name="properties" Type=IDictionary<string, object>>Addition message information</param>        
        /// <param name="message" Type=string>Message to be logged</param>
        public static void WriteWarning(string[] categories, IDictionary<string, object> properties, string message)
        {
            EntLog.Logger.Write(message, categories, (int)Priority.Medium, DefaultEventId, TraceEventType.Warning, WarnTitle, properties);
        }

        #endregion
               
        #region Enterprise Library Common Logger Methods

        /// <summary>
        /// Add a key/value pair to the <see cref="System.Runtime.Remoting.Messaging.CallContext"/> dictionary.  
        /// Context items will be recorded with every log entry.
        /// </summary>
        /// <param name="key">Hashtable key</param>
        /// <param name="value">Value.  Objects will be serialized.</param>
        /// <example>The following example demonstrates use of the AddContextItem method.
        /// <code>Logger.SetContextItem("SessionID", myComponent.SessionId);</code></example>
        public static void SetContextItem(object key, object value)
        {
            EntLog.Logger.SetContextItem(key, value);
        }

        /// <summary>
        /// Empty the context items dictionary.
        /// </summary>
        public static void FlushContextItems()
        {
            EntLog.Logger.FlushContextItems();
        }

        /// <summary>
        /// Write a new log entry as defined in the <see cref="LogEntry"/> parameter.
        /// </summary>
        /// <param name="log">Log entry object to write</param>
        public static void Write(LogEntry log)
        {
            EntLog.Logger.Writer.Write(log);
        }

        /// <summary>
        /// Write a new log entry with a specific category, priority, event Id, severity
        /// title and dictionary of extended properties.
        /// </summary>
        /// <param name="message">Message body to log.  Value from ToString() method from message object.</param>
        /// <param name="categories">Category names used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="eventId">Event number or identifier.</param>
        /// <param name="severity">Log message severity as a <see cref="TraceEventType"/> enumeration. (Unspecified, Information, Warning or Error).</param>
        /// <param name="title">Additional description of the log entry message.</param>
        /// <param name="properties">Dictionary of key/value pairs to log.</param>
        public static void Write(object message, string[] categories, Priority priority, int eventId,
                                 TraceEventType severity, string title, IDictionary<string, object> properties)
        {
            LogEntry log = new LogEntry();
            log.Message = message.ToString();
            log.Categories = categories;
            log.Priority = (int)priority;
            log.EventId = eventId;
            log.Severity = severity;
            log.Title = title;
            log.ExtendedProperties = properties;

            Write(log);
        }

        /// <summary>
        /// Write a new log entry to the default category.
        /// </summary>
        /// <param name="message">Message body to log.  Value from ToString() method from message object.</param>
        public static void Write(object message)
        {
            Write(message, DefaultCategory, Priority.Unknown,
                  DefaultEventId, DefaultSeverity, DefaultTitle, null);
        }

        /// <summary>
        /// Write a new log entry to a specific category.
        /// </summary>
        /// <param name="message">Message body to log.  Value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        public static void Write(object message, string[] category)
        {
            Write(message, category, DefaultPriority, DefaultEventId, DefaultSeverity, DefaultTitle, null);
        }

        /// <summary>
        /// Write a new log entry with a specific category and priority.
        /// </summary>
        /// <param name="message">Message body to log.  Value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        public static void Write(object message, string[] category, Priority priority)
        {
            Write(message, category, priority, DefaultEventId, DefaultSeverity, DefaultTitle, null);
        }

        /// <summary>
        /// Write a new log entry with a specific category, priority and event id.
        /// </summary>
        /// <param name="message">Message body to log.  Value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="eventId">Event number or identifier.</param>
        public static void Write(object message, string[] category, Priority priority, int eventId)
        {
            Write(message, category, priority, eventId, DefaultSeverity, DefaultTitle, null);
        }

        /// <summary>
        /// Write a new log entry with a specific category, priority, event id and severity.
        /// </summary>
        /// <param name="message">Message body to log.  Value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="eventId">Event number or identifier.</param>
        /// <param name="severity">Log entry severity as a <see cref="TraceEventType"/> enumeration. (Unspecified, Information, Warning or Error).</param>
        public static void Write(object message, string[] category, Priority priority, int eventId, TraceEventType severity)
        {
            Write(message, category, priority, eventId, severity, DefaultTitle, null);
        }

        /// <summary>
        /// Write a new log entry with a specific category, priority, event id, severity
        /// and title.
        /// </summary>
        /// <param name="message">Message body to log.  Value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="eventId">Event number or identifier.</param>
        /// <param name="severity">Log message severity as a <see cref="TraceEventType"/> enumeration. (Unspecified, Information, Warning or Error).</param>
        /// <param name="title">Additional description of the log entry message</param>
        public static void Write(object message, string[] category, Priority priority, int eventId,
                                 TraceEventType severity, string title)
        {
            Write(message, category, priority, eventId, severity, title, null);
        }

        /// <summary>
        /// Write a new log entry and a dictionary of extended properties.
        /// </summary>
        /// <param name="message">Message body to log.  Value from ToString() method from message object.</param>
        /// <param name="properties">Dictionary of key/value pairs to log.</param>
        public static void Write(object message, IDictionary<string, object> properties)
        {
            Write(message, DefaultCategory, DefaultPriority,
                  DefaultEventId, DefaultSeverity, DefaultTitle, properties);
        }

        /// <summary>
        /// Write a new log entry to a specific category with a dictionary 
        /// of extended properties.
        /// </summary>
        /// <param name="message">Message body to log.  Value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        /// <param name="properties">Dictionary of key/value pairs to log.</param>
        public static void Write(object message, string[] category, IDictionary<string, object> properties)
        {
            Write(message, category, DefaultPriority, DefaultEventId, DefaultSeverity,
                  DefaultTitle, properties);
        }

        /// <summary>
        /// Write a new log entry to with a specific category, priority and a dictionary 
        /// of extended properties.
        /// </summary>
        /// <param name="message">Message body to log.  Value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="properties">Dictionary of key/value pairs to log.</param>
        public static void Write(object message, string[] category, Priority priority, IDictionary<string, object> properties)
        {
            Write(message, category, priority, DefaultEventId, DefaultSeverity, DefaultTitle, properties);
        }

        /// <summary>
        /// Returns the filter of type <typeparamref name="T"/>.
        /// </summary>
        /// <typeparam name="T">The type of filter requiered.</typeparam>
        /// <returns>The instance of <typeparamref name="T"/> in the filters collection, or <see langword="null"/> 
        /// if there is no such instance.</returns>
        public static T GetFilter<T>()
            where T : class, ILogFilter
        {
            return EntLog.Logger.Writer.GetFilter<T>();
        }

        /// <summary>
        /// Returns the filter of type <typeparamref name="T"/> named <paramref name="name"/>.
        /// </summary>
        /// <typeparam name="T">The type of filter required.</typeparam>
        /// <param name="name">The name of the filter required.</param>
        /// <returns>The instance of <typeparamref name="T"/> named <paramref name="name"/> in 
        /// the filters collection, or <see langword="null"/> if there is no such instance.</returns>
        public static T GetFilter<T>(string name)
            where T : class, ILogFilter
        {
            return EntLog.Logger.Writer.GetFilter<T>(name);
        }

        /// <summary>
        /// Returns the filter named <paramref name="name"/>.
        /// </summary>
        /// <param name="name">The name of the filter required.</param>
        /// <returns>The filter named <paramref name="name"/> in 
        /// the filters collection, or <see langword="null"/> if there is no such filter.</returns>
        public static ILogFilter GetFilter(string name)
        {
            return Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Writer.GetFilter(name);
        }

        /// <summary>
        /// Query whether logging is enabled.
        /// </summary>
        /// <returns><code>true</code> if logging is enabled.</returns>
        public static bool IsLoggingEnabled()
        {
            return Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Writer.IsLoggingEnabled();
        }

        /// <summary>
        /// Query whether a <see cref="LogEntry"/> shold be logged.
        /// </summary>
        /// <param name="log">The log entry to check</param>
        /// <returns>Returns <code>true</code> if the entry should be logged.</returns>
        public static bool ShouldLog(LogEntry log)
        {
            return Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Writer.ShouldLog(log);
        }

        /// <summary>
        /// Gets the instance of <see cref="LogWriter"/> used by the facade.
        /// </summary>
        /// <remarks>
        /// The lifetime of this instance is managed by the facade.
        /// </remarks>
        public static LogWriter Writer
        { get { return Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Writer; } }

        #endregion
    }
}
